using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Xfinity.Engine;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Xfinity.Settings;
using Microsoft.Xna.Framework.Content;

namespace Xfinity.Console
{
    /// <summary>
    /// A console.
    /// </summary>
    public class Console : DrawableGameComponent
    {
        #region Console trace listener
        /// <summary>
        /// A console trace listener.
        /// </summary>
        public class ConsoleTraceListener : TraceListener
        {
            /// <summary>
            /// Writes a message.
            /// </summary>
            /// <param name="message">The message.</param>
            public override void Write(string message)
            {
                StandardConsole.Write(message);
            }
            /// <summary>
            /// Writes a message.
            /// </summary>
            /// <param name="message">The message.</param>
            public override void WriteLine(string message)
            {
                StandardConsole.WriteLine(message);
            }
        }
        #endregion
        private ContentManager content;
        private static SpriteFont font;
        private static SpriteBatch batch;
        private static GraphicsDevice device;
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="game">The game.</param>
        public Console(Game game)
            : base(game)
        {
            DrawOrder = int.MaxValue;
            Visible = false;
            game.Components.Add(this);

        }
        protected override void LoadGraphicsContent(bool loadAllContent)
        {
            if (loadAllContent)
            {
                device = GraphicsDevice;
                content = new ContentManager(Game.Services);
                batch = new SpriteBatch(GraphicsDevice);
                font = content.Load<SpriteFont>(@"Content\Fonts\Consolas-12");
            }
            base.LoadGraphicsContent(loadAllContent);
        }
        /// <summary>
        /// Called on visibility changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="args">The args.</param>
        protected override void OnVisibleChanged(object sender, EventArgs args)
        {
            GraphicsHelper.DrawModels = !Visible;
            GraphicsHelper.BloomEnabled = !Visible;
            base.OnVisibleChanged(sender, args);
        }
        /// <summary>
        /// Draws the console.
        /// </summary>
        /// <param name="gameTime">The elapsed time.</param>
        public override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(EngineSettings.GetConvertedConsoleColour());

            if (StandardConsole.Transcript.Count <= 0 && StandardConsole.InputLine.Length == 0)
            {
                return;
            }
            batch.Begin();
            int numberOfLinesFitting = ComputeNumberOfLinesFitting();
            int linesToDraw = ComputeLinesToDraw(numberOfLinesFitting);
            int nextYPos = 0;            
            //draw each of the visible transcript items
            nextYPos = DrawTranscript(linesToDraw, nextYPos);
            DrawInputLine(nextYPos);
            batch.End();
            GraphicsDevice.RenderState.DepthBufferEnable = true;
            base.Draw(gameTime);
        }

        private static int DrawTranscript(int linesToDraw, int nextYPos)
        {
            for (int i = linesToDraw; i > 0; i--)
            {
                DrawTranscriptLine(nextYPos, i);
                nextYPos += font.LineSpacing;
            }
            return nextYPos;
        }

        private static int ComputeNumberOfLinesFitting()
        {
            int numberOfLinesFitting = (int)(device.Viewport.Height / font.LineSpacing);
            return numberOfLinesFitting;
        }

        private static int ComputeLinesToDraw(int numberOfLinesFitting)
        {
            int linesToDraw;
            if (StandardConsole.Transcript.Count < numberOfLinesFitting)
            {
                linesToDraw = StandardConsole.Transcript.Count;
            }
            else //make sure it fits!
            {
                linesToDraw = numberOfLinesFitting - 1;
            }
            return linesToDraw;
        }

        private static void DrawTranscriptLine(int nextYPos, int i)
        {
            string text = StandardConsole.Transcript[StandardConsole.Transcript.Count - i];//- StandardConsole.BottomViewLineOffset];
            Color c = ComputeTextColour(i);
            DrawText(text, new Vector2(10, nextYPos), c);
        }
        [DebuggerStepThrough]
        private static void DrawText(string text, Vector2 topLeft, Color color)
        {
            batch.DrawString(font, text, topLeft, color);
        }
        private static Color ComputeTextColour(int i)
        {
            Color c;
            if (StandardConsole.IsFailMessage[StandardConsole.IsFailMessage.Count - i])//- StandardConsole.BottomViewLineOffset])
            {
                c = Color.Red;
            }
            else
            {
                c = Color.White;
            }
            return c;
        }

        private void DrawInputLine(int nextYPos)
        {
            //draw input line
            string input = StandardConsole.InputLine.ToString();

            Vector2 origin = ComputeOrigin(GraphicsDevice.Viewport.Height - font.LineSpacing, input);
            DrawCaret(origin);
            if (!String.IsNullOrEmpty(StandardConsole.TabCompletedLine))
            {
                DrawTabCompletedLine(input, origin);
            }
            else
            {
                DrawText(input, origin, Color.White);
            }

        }

        private static void DrawTabCompletedLine(string input, Vector2 origin)
        {
            string tab = StandardConsole.TabCompletedLine;
            string highlighted = tab.Substring(input.Length);
            Vector2 highlightSize = font.MeasureString(input);
            DrawText(input, origin, Color.White);
            origin.X += highlightSize.X;
            DrawText(highlighted, origin, Color.Aquamarine);
        }

        private Vector2 ComputeOrigin(int nextYPos, string input)
        {
            Vector2 size = font.MeasureString(input);
            Vector2 origin;
            if (size.X > GraphicsDevice.Viewport.Width - 10)
            {
                origin = new Vector2(GraphicsDevice.Viewport.Width - size.X, nextYPos);
            }
            else
            {
                origin = new Vector2(10, nextYPos);
            }
            return origin;
        }

        private static void DrawCaret(Vector2 origin)
        {
            Vector2 caret = origin;
            caret.X = 1;
            DrawText(">", caret, Color.White);
            //return origin;
        }
    }
}